﻿using System;
using System.Collections.Generic;

using System.Text;
using System.Drawing;
using ITAP.ShprossesBuilder.Modelling;
using ITAP.ShprossesBuilder.Modelling.Components;
using System.Windows.Forms;
using ITAP.ShprossesBuilder.Calculations;
using ITAP.Core.Modelling;
using ITAP.Core.Calculations;

namespace ITAP.ShprossesBuilder.Manipulation
{
    public class CommandMoveComponent : IModelComponentCommand, IModelVisitor
    {
        IComponent componentToMove;
        IComponent componentToMoveStart;
        IComponent componentToMoveEnd;

        private RectangleF activeModelBoundingBox;
        private Point mouseDownPoint;
        private Point mouseMovePoint;
        private Point prevMouseMovePoint;
        private Point mouseUpPoint;
        private ModelControl modelControl;
        private List<Type> typesToMove;
        private string name;
        private MouseManagerEvent mouseEvent;
        private Model model;
        private bool canUndo = true;
        private bool isMoved = false;
        public bool CanUndo
        {
            get
            {
                return canUndo;
            }
        }

        public CommandMoveComponent(ModelControl modelControl, Type[] typesToMove, string name)
        {
            this.typesToMove = new List<Type>(typesToMove);
            this.modelControl = modelControl;
            this.name = name;

        }

        #region IModelComponentCommand Members

        public List<ITAP.ShprossesBuilder.Modelling.Components.IComponent> ComponentsForProcessing
        {
            get
            {
                List<IComponent> components = new List<IComponent>();
                components.Add(componentToMove);
                return components;
            }
            set
            {
                componentToMove = value[0];
            }
        }

        #endregion

        #region IModelControlCommand Members

        public MouseManagerEvent MouseEvent
        {
            get
            {
                return mouseEvent;
            }
            set
            {
                mouseEvent = value;
            }
        }

        public ModelControl ModelControl
        {
            get
            {
                return modelControl;
            }
            set
            {
                modelControl = value;
            }
        }

        public System.Drawing.Point MouseDownPoint
        {
            get
            {
                return mouseDownPoint;
            }
            set
            {
                mouseDownPoint = value;
            }
        }

        public System.Drawing.Point MouseUpPoint
        {
            get
            {
                return mouseUpPoint;
            }
            set
            {
                mouseUpPoint = value;
            }
        }

        public System.Drawing.Point MouseMovePoint
        {
            get
            {
                return mouseMovePoint;
            }
            set
            {
                mouseMovePoint = value;
            }
        }

        public bool ExecuteImmediately
        {
            get
            {
                return false;
            }
            set
            {
            }
        }

        #endregion

        private void Move()
        {
            Point offsetPoint = new Point(mouseMovePoint.X - prevMouseMovePoint.X, mouseMovePoint.Y - prevMouseMovePoint.Y);
            RectangleF bbox = modelControl.ModelContainer.BoundingBox;
            double modelXOffset = modelControl.CoordinateConverter.ScreenMeasureToModelMeasure(offsetPoint.X, modelControl.ClientRectangle.Width, modelControl.ClientRectangle.Height, bbox);
            double modelYOffset = modelControl.CoordinateConverter.ScreenMeasureToModelMeasure(offsetPoint.Y, modelControl.ClientRectangle.Width, modelControl.ClientRectangle.Height, bbox);
            ModelVector vector = new ModelVector(modelXOffset, -modelYOffset);
            if (!SimpleEqualProvider.IsEqual(vector.Length, 0, 0.00001))
            {
                isMoved = true;
                componentToMove.MoveWithOffset(vector, activeModelBoundingBox);

                prevMouseMovePoint = mouseMovePoint;
            }
        }

        private void ChangeComponentParameters()
        {
            componentToMove.AcceptVisitor(this);
        }

        #region ICommand Members

        public void StartExecute()
        {
            isMoved = false;
            componentToMove = null;
            ModelCell activeCell = modelControl.ModelContainer.ActiveCell as ModelCell;
            if (activeCell == null)
                this.activeModelBoundingBox = RectangleF.Empty;
            else
            {
                this.model = activeCell.Model;
                this.activeModelBoundingBox = activeCell.Model.BoundingBox;
            }


            ModelPoint modelPoint = modelControl.ScreenToModel(mouseDownPoint);
            List<IComponent> components = modelControl.ModelContainer.GetComponentsByPoint(modelPoint, 5);

            modelControl.ModelContainer.SelectByPoint(modelPoint);
            foreach (IComponent component in components)
                if (typesToMove.Contains(component.GetType()))
                {
                    //foreach (IComponent innerComponent in component.AllInnerComponents)
                    //    innerComponent.IsUpdateRequired = true;
                    //component.IsUpdateRequired = true;
                    componentToMove = component;
                }


            if (componentToMove == null)
                return;


            if (!componentToMove.Selected)
            {
                componentToMove = null;
                return;
            }

            componentToMoveStart = componentToMove.Copy();
            prevMouseMovePoint = mouseDownPoint;

            if (mouseEvent.Key == (Keys.Menu | Keys.Alt))
                componentToMove.AcceptVisitor(this);
            if (isMoved)
                modelControl.ModelContainer.Update();

        }

        public void Execute()
        {

            if (componentToMove == null)
                return;
            componentToMove.AcceptVisitor(this);
            if (isMoved)
                modelControl.ModelContainer.Update();
            else
            {
            }
        }

        public void EndExecute()
        {
            if (componentToMove == null)
                canUndo = false;
            else
                canUndo = true;

            if (componentToMove != null)
                componentToMoveEnd = componentToMove.Copy();
        }

        public string Name
        {
            get { return "Движение элемента"; }
        }

        #endregion

        #region ICloneable Members

        public object Clone()
        {
            CommandMoveComponent clone = new CommandMoveComponent(modelControl, typesToMove.ToArray(), name);
            clone.MouseEvent = this.MouseEvent;
            clone.MouseDownPoint = this.MouseDownPoint;
            clone.MouseMovePoint = this.MouseMovePoint;
            clone.MouseUpPoint = this.MouseUpPoint;

            clone.componentToMove = this.componentToMove;
            if (componentToMoveStart != null)
                clone.componentToMoveStart = this.componentToMoveStart.Copy();

            if (componentToMoveEnd != null)
                clone.componentToMoveEnd = this.componentToMoveEnd.Copy();
            clone.model = this.model;
            clone.prevMouseMovePoint = this.prevMouseMovePoint;

            return clone;
        }

        #endregion

        #region IModelVisitor Members

        public void Visit(Model model)
        {
            throw new NotImplementedException();
        }

        public void Visit(Connector connector)
        {
            throw new NotImplementedException();
        }

        private void MoveShpross(Shpross shpross)
        {
            this.Move();
        }

        private void ChangeShprossParameters(Shpross shpross)
        {
            ModelPoint currentModelPoint = modelControl.ScreenToModel(prevMouseMovePoint);
            Point offsetPoint = new Point(mouseMovePoint.X - prevMouseMovePoint.X, mouseMovePoint.Y - prevMouseMovePoint.Y);
            RectangleF bbox = modelControl.ModelContainer.BoundingBox;
            double modelXOffset = modelControl.CoordinateConverter.ScreenMeasureToModelMeasure(offsetPoint.X, modelControl.ClientRectangle.Width, modelControl.ClientRectangle.Height, bbox);
            double modelYOffset = modelControl.CoordinateConverter.ScreenMeasureToModelMeasure(offsetPoint.Y, modelControl.ClientRectangle.Width, modelControl.ClientRectangle.Height, bbox);
            ModelVector vector = new ModelVector(modelXOffset, -modelYOffset);
            if (!SimpleEqualProvider.IsEqual(vector.Length, 0, 0.00001))
                isMoved = true;

            if (shpross.SelectedPoint == ShprossPoints.Start)
            {
                {
                    if (!(shpross.StartConnectedComponent is SpacerConnector))
                        if (!shpross.RemoveInnerComponent(shpross.StartConnectedComponent))
                            ConnectionProvider.Disconnect(shpross, shpross.StartConnectedComponent);
                }

                shpross.MoveStartPointTo(new ModelPoint(shpross.StartPoint.X + vector.X, shpross.StartPoint.Y + vector.Y), activeModelBoundingBox, false);
            }
            //else if (shprossPoint == ShprossPoints.End)
            else if (shpross.SelectedPoint == ShprossPoints.End)
            {
                if(!(shpross.EndConnectedComponent is SpacerConnector))
                {
                    if (!shpross.RemoveInnerComponent(shpross.EndConnectedComponent))
                        ConnectionProvider.Disconnect(shpross, shpross.EndConnectedComponent);
                }
                shpross.MoveEndPointTo(new ModelPoint(shpross.EndPoint.X + vector.X, shpross.EndPoint.Y + vector.Y), activeModelBoundingBox, false);
            }
            else if (shpross.SelectedPoint == ShprossPoints.Center)
            {
                if (mouseEvent.Key != (Keys.ControlKey | Keys.Control))
                {
                    this.MoveShpross(shpross);
                }
                else
                {
                    double heightDy = modelControl.CoordinateConverter.ScreenMeasureToModelMeasure(mouseMovePoint.Y - prevMouseMovePoint.Y, modelControl.ClientRectangle.Width, modelControl.ClientRectangle.Height, bbox);
                    double heightDx = modelControl.CoordinateConverter.ScreenMeasureToModelMeasure(mouseMovePoint.X - prevMouseMovePoint.X, modelControl.ClientRectangle.Width, modelControl.ClientRectangle.Height, bbox);

                    if (!SimpleEqualProvider.IsEqual(heightDx + heightDy, 0, 0.00001))
                        isMoved = true;

                    shpross.SetHeight(shpross.Height + heightDx + heightDy, activeModelBoundingBox);
                }
            }

            prevMouseMovePoint = mouseMovePoint;

        }


        public void Visit(Shpross shpross)
        {
            //List<IComponent> componentsToDisconnect = new List<IComponent>();
            //foreach (IComponent connectedComponent in shpross.ConnectedComponents)                
            //    componentsToDisconnect.Add(connectedComponent);

            //  shpross.ValidateSpacerConnectorsPositions();

            //if (mouseEvent.Key != (Keys.Control | Keys.ControlKey))
            //    this.MoveShpross(shpross);
            //else
            this.ChangeShprossParameters(shpross);

        }

        private ShprossPoints GetCurrentShprossPoint(Shpross shpross, ModelPoint currentModelPoint)
        {
            double startTolerance =
                ModelCalculations.GetDistance(shpross.Primitives[0].GetPoint(0), shpross.Primitives[0].GetPoint(0.3));

            double centerTolerance =
                ModelCalculations.GetDistance(shpross.Primitives[0].GetPoint(0), shpross.Primitives[0].GetPoint(0.3));

            double endTolerance =
                ModelCalculations.GetDistance(shpross.Primitives[0].GetPoint(0), shpross.Primitives[0].GetPoint(0.3));


            if (ModelCalculations.GetDistance(currentModelPoint, shpross.StartPoint) < startTolerance)
                return ShprossPoints.Start;
            else if (ModelCalculations.GetDistance(currentModelPoint, shpross.EndPoint) < centerTolerance)
                return ShprossPoints.End;
            else if (ModelCalculations.GetDistance(currentModelPoint, shpross.Primitives[0].GetPoint(0.5)) < endTolerance)
                return ShprossPoints.Center;
            else
                return ShprossPoints.None;
        }

        public void Visit(Spacer spacer)
        {
            throw new NotImplementedException();
        }

        public void Visit(ShprossConnectorStub stub)
        {
            throw new NotImplementedException();
        }

        private void MoveShprossConnector(ShprossConnector shprossConnector)
        {
            this.Move();
        }

        private void ChangeShprossConnectorParameters(ShprossConnector shprossConnector)
        {
            ModelPoint currentModelPoint = modelControl.ScreenToModel(prevMouseMovePoint);
            Point offsetPoint = new Point(mouseMovePoint.X - prevMouseMovePoint.X, mouseMovePoint.Y - prevMouseMovePoint.Y);
            RectangleF bbox = modelControl.ModelContainer.BoundingBox;
            double modelYOffset = modelControl.CoordinateConverter.ScreenMeasureToModelMeasure(offsetPoint.Y, modelControl.ClientRectangle.Width, modelControl.ClientRectangle.Height, bbox);

            if (modelYOffset != 0)
            {
                isMoved = true;
                shprossConnector.Angle = new Angle(shprossConnector.Angle.Value + modelYOffset / 100);
                prevMouseMovePoint = mouseMovePoint;
            }
        }

        private void ShprossConnectorDiscreteRotateClockwise(ShprossConnector shprossConnector)
        {
            if (this.mouseEvent.MouseMoveType == MouseMoveType.MouseDown)
            {
                shprossConnector.Rotate(-Math.PI / 4);
                this.isMoved = true;
            }
        }

        private void ShprossConnectorDiscreteRotateAnticlockwise(ShprossConnector shprossConnector)
        {
            if (this.mouseEvent.MouseMoveType == MouseMoveType.MouseDown)
            {
                shprossConnector.Rotate(Math.PI / 4);
                this.isMoved = true;
            }
        }

        public void Visit(ShprossConnector shprossConnector)
        {
            if (mouseEvent.Key == (Keys.ControlKey | Keys.Control))          
                this.ChangeShprossConnectorParameters(shprossConnector);
            else if (mouseEvent.MouseButton == MouseButtons.Left && mouseEvent.Key == (Keys.Menu | Keys.Alt))
                this.ShprossConnectorDiscreteRotateAnticlockwise(shprossConnector);
            else if (mouseEvent.MouseButton == MouseButtons.Right && mouseEvent.Key == (Keys.Menu | Keys.Alt))
                this.ShprossConnectorDiscreteRotateClockwise(shprossConnector);
            else
                this.MoveShprossConnector(shprossConnector);
        }

        public void Visit(SpacerElement spacerElement)
        {
            throw new NotImplementedException();
        }

        public void Visit(SpacerConnector spacerConnector)
        {
            throw new NotImplementedException();
        }

        public void Visit(ModelCell modelCell)
        {
            throw new NotImplementedException();
        }

        public void Visit(ModelContainer modelContainer)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region ICommand Members


        public bool IsNeedModelUpdate
        {
            get { return true; }
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(StickConnector stickConnector)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(SnapOnCap snapOnCap)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(ShprossEndToEndConnector endToEndConnector)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(ShprossChain shprossChain)
        {
            //  throw new NotImplementedException();
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(SpacerContainer spacerContainer)
        {
            //  throw new NotImplementedException();
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(CrossConnector crossConnector)
        {
            //     throw new NotImplementedException();
        }

        #endregion

        #region ICommand Members

        public bool Undo(ModelControl modelControl)
        {
            IComponent parent = modelControl.ModelContainer.GetParentComponentByComponentSerialNumber(componentToMove.SerialNumber);
            modelControl.ModelContainer.RemoveInnerComponentBySerialNumber(componentToMove.SerialNumber);
            if (componentToMove is Shpross)
            {
                //Shpross shpross = componentToMove as Shpross;
                Shpross shpross = this.componentToMoveStart.Copy() as Shpross;
                //shpross.MoveStartPointTo(shpross.StartPoint);
                //shpross.MoveEndPointTo(shpross.EndPoint);
                //shpross.Height = shpross.Height;
                parent.AddInnerComponent(shpross);
                return true;
            }
            else if (componentToMove is ShprossConnector)
            {
                //ShprossConnector connector = componentToMove as ShprossConnector;
                ShprossConnector undoConnector = componentToMoveStart.Copy() as ShprossConnector;
                parent.AddInnerComponent(undoConnector);
                //connector.MoveTo(undoConnector.Position);
                //connector.Angle = undoConnector.Angle.Clone() as Angle;
                return true;
            }
            return false;
        }

        public bool Redo(ModelControl modelControl)
        {
            // componentToMove.DisconnectFromAll();

            IComponent parent = modelControl.ModelContainer.GetParentComponentByComponentSerialNumber(componentToMove.SerialNumber);
            modelControl.ModelContainer.RemoveInnerComponentBySerialNumber(componentToMove.SerialNumber);

            if (componentToMove is Shpross)
            {
                //                Shpross shpross = componentToMove as Shpross;
                Shpross redoShpross = this.componentToMoveEnd.Copy() as Shpross;

                //shpross.MoveStartPointTo(redoShpross.StartPoint);
                //shpross.MoveEndPointTo(redoShpross.EndPoint);
                //shpross.Height = redoShpross.Height;
                parent.AddInnerComponent(redoShpross);
                return true;
            }
            else if (componentToMove is ShprossConnector)
            {
                //                ShprossConnector connector = componentToMove as ShprossConnector;
                ShprossConnector redoConnector = this.componentToMoveEnd.Copy() as ShprossConnector;
                parent.AddInnerComponent(redoConnector);
                //connector.MoveTo(redoConnector.Position);
                //connector.Angle = redoConnector.Angle.Clone() as Angle;
                return true;
            }
            return false;
        }


        #endregion

        public override string ToString()
        {
            return this.Name;
        }

        #region ICommand Members


        private bool enabled = true;
        public bool Enabled
        {
            get
            {
                return enabled;
            }
            set
            {
                enabled = value;
            }
        }
        #endregion

        #region ICommand Members


        public bool IsNeedVisitorsUpdate
        {
            get
            {
                //return true;
                return isMoved;
            }
        }

        #endregion
    }
}
